package pt.inescid.components.messages;
import java.io.IOException;
import java.util.Vector;

import rice.pastry.NodeSet;
import rice.p2p.commonapi.Id;
import rice.p2p.commonapi.Message;
import rice.p2p.commonapi.Endpoint;
import rice.p2p.commonapi.NodeHandle;
import rice.p2p.commonapi.NodeHandleSet;
import rice.p2p.commonapi.rawserialization.*;

/**
 * 
 * Update Message - sent by neighbor nodes to update
 *                  their current availability
 * 
 * @author Filipe Paredes
 * @author Sergio Esteves (sesteves at gsd.inesc-id.pt)
 */
public class UpdateMsg implements RawMessage {	
	public static final short TYPE = 2;

	protected final static String SEPARAT_CHR = "###";
	
	private NodeHandleSet source;

	/**
	 * Availability of the node: cpu, memory & bandwith.
	 * Alternative - if this node has nodes with reliable reputation
	 */
	private double cpu, mem, bw, alternative = 0;
	private long storage;

	// Application identifiers, each separated by the SEPARAT_CHR
	private String appIDs;
	
	// minimum ratio required
	private double minRatio;
	
	// Defines if the resources in this message correspond to increase/decrease
	private boolean consume;
	
	// Time in future the cost will be restored
	private long restoreTime = 0;
	
	// Sequence Number - to guarantee the most recent of updates	
	private long seqNumber;

	private static final long serialVersionUID = 7526472295622776147L;

	private String arrayToString(String[] arr) {
		if(arr == null) return null;
		String finalStr = "";		
		for(String str : arr) {
			finalStr = finalStr + SEPARAT_CHR + str;
		}
		return finalStr;
	}		
	
	/**
	 * Constructors
	 * 
	 * The 1st constructor gives the choice of share the existence of reliable nodes
	 */
	public UpdateMsg(NodeHandle source, double cpu, double mem, double bw, long storage, 
			String[] appIDs, boolean consume, long seqNumber) {
		this.source = new NodeSet();
		this.source.putHandle(source);
		this.cpu = cpu;
		this.mem = mem;
		this.bw = bw;
		this.storage = storage;
		this.appIDs = arrayToString(appIDs);
		this.consume = consume;
		this.seqNumber = seqNumber;
	}

	public UpdateMsg(NodeHandle source, double cpu, double mem, double bw, long storage,
			String[] appIDs, boolean consume, double alternative, long seqNumber) {
		this(source,cpu,mem,bw,storage,appIDs,consume,seqNumber);
		this.alternative = alternative;
	}

	public void setRestoreTime(long restoreTime) {
		this.restoreTime = restoreTime;
	}
	public void setMinRatio(double ratio) {
		minRatio = ratio;
	}
	
	public double getCpu(){
		return cpu;
	}
	public double getMem(){
		return mem;
	}
	public double getBW(){
		return bw;
	}
	public long getStorage() {
		return storage;
	}
	public String[] getAppIDs() {
		return appIDs.split(SEPARAT_CHR);	
	}
	public double getMinRatio() {
		return minRatio;
	}
	
	public boolean getConsume() {
		return consume;
	}

	public Vector<Double> getCost() {
		Vector<Double> cost = new Vector<Double>();
		cost.add(cpu);
		cost.add(mem);
		cost.add(bw);
		return cost;
	}

	public long getRestoreTime(){
		return restoreTime;
	}

	public double getAlt(){
		return alternative;
	}

	public long getSeqNumber() {
		return seqNumber;
	}

	public Id getSource(){
		return source.getHandle(0).getId();
	}

	public NodeHandle getSourceHandle(){
		return source.getHandle(0);
	}

	public String toString() {
		return "Update message of node: " + source + ", cpu: " + cpu + ", mem: " +
			mem + ", bw: " + bw + ", storage: " + storage + ", appIDs: " + appIDs +
			", minRatio: " + minRatio + ". Expire in time: " + restoreTime;
	}

	/**
	 * Use low priority to prevent interference with overlay maintenance traffic.
	 */
	public int getPriority() {
		return Message.LOW_PRIORITY;
	}

	public short getType() {
		return TYPE;
	}

	/**
	 * Deserializing constructor.  This does the "real" deserialization.
	 * 
	 * @param buf
	 * @param endpoint
	 * @throws IOException
	 */
	public UpdateMsg(InputBuffer buf, Endpoint endpoint) throws IOException {
		// Deserialize the fields
		source = endpoint.readNodeHandleSet(buf, buf.readShort());
		cpu = buf.readDouble();
		mem = buf.readDouble();
		bw = buf.readDouble();
		storage = buf.readLong();
		appIDs = buf.readUTF();		
		minRatio = buf.readDouble();
		consume = buf.readBoolean();
		restoreTime = buf.readLong();
		alternative = buf.readDouble();
		seqNumber = buf.readLong();
	}

	public void serialize(OutputBuffer buf) throws IOException {
		// Serialize this instance data
		buf.writeShort(source.getType());
		source.serialize(buf);
		buf.writeDouble(cpu);
		buf.writeDouble(mem);
		buf.writeDouble(bw);
		buf.writeLong(storage);
		buf.writeUTF(appIDs);
		buf.writeDouble(minRatio);
		buf.writeBoolean(consume);
		buf.writeLong(restoreTime);
		buf.writeDouble(alternative);
		buf.writeLong(seqNumber);
	}
}
